/*
 * @Date: 2020-10-06 12:13:50
 * @Author: David
 * @LastEditors: OBKoro1
 * @LastEditTime: 2020-10-06 21:45:20
 * @FilePath: /huanyong-task-01-01/code/myPromise.js
 * @Description: someting...
 */
/**
 * Promise 功能实现
 * promise 对象有三种状态: fulfilled 完成，rejected 拒绝，padding 等待中。 
 * 状态如果不是 padding，那么将不会再发生改变
 * 1. 构造方法
 * 2. resolve ,reject 
 * 3. then 
 * 4. catch 
 * 5. 静态方法 resolve 、reject、 all、race、allSettled
 * 6. finally 
 * 
 */
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PADDING = 'padding';
class MyPromise {
    status = PADDING; // 当前 promise 的状态
    successCallback = []; // 存放成功回调
    failCallback = []; // 存放失败回调
    value = null;
    reason = null;
    // 执行回调
    constructor(executor) {
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject.bind(this)(e);
        }
    }
    // 将状态切换为 fulfilled，并执行, 如果状态不是 padding 则不执行
    resolve(value) {
        if (this.status === PADDING) {
            this.status = FULFILLED;
            this.value = value;
            while (this.successCallback.length > 0) this.successCallback.shift()();
        }
    }
    // 将状态切换成 rejected，如果状态不是 padding 则不执行
    reject(reason) {
        if (this.status === PADDING) {
            this.status = REJECTED;
            this.reason = reason;
            while (this.failCallback.length > 0) this.failCallback.shift()();
        }
    }
    /**
     * 创建一个新的 promise对象，并处理传入的 回调，返回一个全新的 promise，
     * 1. 如果 then 方法不传入成功 或失败回调，则会设置默认回调
     * 2. then 方法会返回一个 全新的promise对象，这个 新的promise对象 在当前 promise对象状态 发生改变时，才会改变状态。
     * 3. 如果当前 的 promise对象的状态已经发生改变,则会直接执行 then 的回调，如果未发生改变，则会将传入的 成功 和 失败回调 存储起来，等待状态改变后执行。
     * 4. 在改变状态为 fulfilled 时，会先判断当前的 结果值 `value` 是 promise 对象 还是 普通值，如果是普通值，则会直接调用 resolve，如果是 promise对象，则会调用 其 then 方法，因为我们需要等待当前的 promise 状态发生改变才能拿到最终的结果值，然后传给新的promise
     * @param {*} successCallback 
     * @param {*} failCallback 
     */
    then(successCallback, failCallback) {
        // 如果 then 方法不传参，则设置默认回调，将 值 和 错误原因传递给下一个 promise
        successCallback = typeof successCallback === 'function' ? successCallback : value => value;
        failCallback = typeof failCallback === 'function' ? failCallback : reason => {
            throw reason
        };
        const promise = new MyPromise((resolve, reject) => {
            try {
                //执行成功回调
                if (this.status === FULFILLED) {
                    // 如果状态为 完成，则调用成功回调，并将返回值存储
                    this.value = successCallback(this.value);
                    // 这里setTimeout 是因为目前还在执行构造函数， 新的 promise对象还未创建完成，如果下面直接赋值，promise 这个变量引用的 还不是 新的 promise对象
                    setTimeout(() => {
                        resolvePromise(promise, this.value, resolve, reject);
                    }, 0);
                } else if (this.status === REJECTED) {
                    // 如果状态为 拒绝，则调用失败回调，并将返回值存储
                    this.reason = failCallback(this.reason);
                    setTimeout(() => {
                        resolvePromise(promise, this.reason, resolve, reject);
                    }, 0);
                } else {
                    //如果仍然是 等待中 ，则将其回调先存储起来，等待状态发生改变时执行
                    this.successCallback.push(() => {
                        // 捕获执行成功回调的
                        try {
                            this.value = successCallback(this.value);
                            setTimeout(() => {
                                resolvePromise(promise, this.value, resolve, reject);
                            }, 0);
                        } catch (e) {
                            this.reason = failCallback(e);
                            setTimeout(() => {
                                resolvePromise(promise, this.reason, resolve, reject);
                            }, 0);
                        }
                    })
                    this.failCallback.push(() => {
                        this.reason = failCallback(this.reason);
                        setTimeout(() => {
                            resolvePromise(promise, this.reason, resolve, reject);
                        }, 0);
                    })
                }
            } catch (e) {
                this.reason = failCallback(e);
                setTimeout(() => {
                    resolvePromise(promise, this.reason, resolve, reject);
                }, 0);
            }
        })
        return promise;
    }
    /**
     * 捕获失败，直接调用 then 函数，传入失败回调即可，返回一个新的 promise对象
     * @param {*} failCallback 
     */
    catch (failCallback) {
        return this.then(undefined, err => {
            failCallback(err);
        });
    }
    /**
     * 无论成功还是失败都会执行，所以调用 then 函数中 ，successCallback 和 failCallback 中都调用了其回调
     * @param {*} callback 
     */
    finally(callback) {
        this.then(() => {
            callback();
        }, () => {
            callback();
        })
    }
    // 返回一个 状态为 fulfilled 的 promise 对象
    static resolve(value) {
        return new MyPromise(resolve => {
            resolve(value);
        })
    }
    // 返回一个 状态为 rejected 的 promise 对象
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason);
        })
    }
    /**
     * 并行处理多个promise对象，并返回一个结果数组，返回结果根据传入时参数顺序决定。
     * 
     * @param {*} array 
     */
    static all(array) {
        return new MyPromise((resolve, reject) => {
            const result = []; //存储结果值
            let index = 0; //标记当前已经改变状态 为 fulfilled 的 promise 对象 的数量

            //用于添加结果值，并监控 promise 是否已经全部处理完成
            function addData(key, data) {
                result[key] = data;
                index++;
                if (index === array.length) {
                    resolve(result);
                }
            }
            try {
                for (let index = 0; index < array.length; index++) {
                    const _promise = array[index];
                    // 如果是 promise 对象，则调用 then方法 ，在改变状态之后 添加结果值到结果数组中
                    if (_promise instanceof MyPromise) {
                        _promise.then(res => {
                            addData(index, res)
                        }, reject)
                    } else {
                        // 如果是普通值，则直接作为结果值存储到结果数组中
                        addData(index, _promise);
                    }
                }
            } catch (e) {
                reject(e);
            }
        })
    }
    /**
     * 传入 promise数组，返回第一个执行完成的 promise 结果 或 失败原因
     * 利用 promise 状态 非 padding 不可改变特性，返回第一个执行完成的promise结果
     * @param {*} array 
     */
    static race(array) {
        return new MyPromise((resolve, reject) => {
            for (let index = 0; index < array.length; index++) {
                const _promise = array[index];
                // 如果是 promise 对象，则调用 then方法 ，在改变状态之后 添加结果值到结果数组中
                if (_promise instanceof MyPromise) {
                    _promise.then(res => {
                        resolve(res);
                    }, reject)
                } else {
                    // 如果是普通值，则直接作为结果值存储到结果数组中
                    resolve(_promise);
                }
            }
        })
    }
    /**
     * 返回执行的结果数组，包含promise对象，等待所有的 promise 执行完成，无论成功或失败
     * @param {*} array 
     */
    static allSettled(array) {
       return new Promise((resolve,reject) => {
        const result = [];
        let index = 0;
        function add(key,promiseObj) {
            result[key] = promiseObj;
            index++;
            if(index == array.length){
                resolve(result);
            }
        }
        for (let index = 0; index < array.length; index++) {
            const promise = array[index];
            if(promise instanceof MyPromise){
               promise.then(res => {add(index,MyPromise.resolve(res))},reason => {
                   add(index,MyPromise.reject(reason));
               })
            }else{
                add(index,MyPromise.resolve(promise))
            }
        }
       })
    }
}
/**
 * 用于改变 当前 传入的 promise 对象的状态
 * 1. 如果传入的 promise 对象和 data 完全一致，则需要抛出异常，避免陷入无限循环
 * 2. 判断传入的 data 是否为 promise 对象，如果是，则调用 then 方法。 等待其状态改变后，才能修改当前的promise状态。
 * 3. 如果是普通值，则直接调用 resolve 修改状态。
 */
function resolvePromise(currentPromise, data, resolve, reject) {
    // 判断当前返回值 是否与传入的 promise 对象相同，避免陷入 无限循环
    if (currentPromise === data) {
        throw TypeError('cycle ...');
    } else if (data instanceof MyPromise) {
        // 如果 传入的值是 promise 对象，则需要等待内部状态 改变后，才能改变当前的 promise 状态
        data.then(resolve, reject);
    } else {
        resolve(data);
    }
}

export default MyPromise